using System;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class InterpolatedStringSpecification {
        [Test]
        public void ShouldParseSimpleString () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""one""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (5, result.Index);
            var term = result.Term as StringTerm;
            Assert.IsNotNull (term);
            Assert.AreEqual ("one", term.Value);
            Assert.AreEqual (new SourceInformation (null, 0, 5), term.SourceInformation);
        }

        [Test]
        public void ShouldNotParseStringWithoutClosingQuote () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""one", 0, new ParseContext ());

            Assert.IsNull (result);
        }

        [Test]
        public void ShouldNotParseEmptyStringWithoutClosingQuote () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""", 0, new ParseContext ());

            Assert.IsNull (result);
        }

        [Test]
        public void ShouldParseStringWithEscapedQuote () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""on\""e""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (7, result.Index);
            var term = result.Term as StringTerm;
            Assert.IsNotNull (term);
            Assert.AreEqual ("on\"e", term.Value);
            Assert.AreEqual (new SourceInformation (null, 0, 7), term.SourceInformation);
        }

        [Test]
        public void ShouldParseStringWithEscapedCharacter () {
            var terminal = new InterpolatedStringProduction (null, c => '!');
            var result = terminal.Parse (@"""on\te""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (7, result.Index);
            var term = result.Term as StringTerm;
            Assert.IsNotNull (term);
            Assert.AreEqual ("on!e", term.Value);
            Assert.AreEqual (new SourceInformation (null, 0, 7), term.SourceInformation);
        }

        [Test]
        public void ShouldParseStringWithEscapedCharacterAtStartAndEnd () {
            var terminal = new InterpolatedStringProduction (null, c => (char) (c + 1));
            var result = terminal.Parse (@"""\tone\n""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (9, result.Index);
            var term = result.Term as StringTerm;
            Assert.IsNotNull (term);
            Assert.AreEqual ("uoneo", term.Value);
            Assert.AreEqual (new SourceInformation (null, 0, 9), term.SourceInformation);
        }

        [Test]
        public void ShouldParseStringInterpolation () {
            var terminal = new InterpolatedStringProduction (new FakeKeywordProduction (3));
            var result = terminal.Parse (@"""on#xxxe""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (9, result.Index);
            var term = result.Term as CompositeTerm;
            Assert.IsNotNull (term);
            var components = term.SubTerms["components"] as ListTerm;
            Assert.IsNotNull (components);
            Assert.AreEqual (3, components.Terms.Count);
            Assert.AreEqual ("on", ((StringTerm) components.Terms [0]).Value);
            Assert.AreEqual ("xxx", ((KeywordTerm) components.Terms [1]).Name);
            Assert.AreEqual ("e", ((StringTerm) components.Terms [2]).Value);
            Assert.AreEqual (new SourceInformation (null, 0, 9), term.SourceInformation);
        }

        [Test]
        public void ShouldParseStringInterpolationAtStartAndEnd () {
            var terminal = new InterpolatedStringProduction (new FakeKeywordProduction (3));
            var result = terminal.Parse (@"""#xxxone#xxx""", 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (13, result.Index);
            var term = result.Term as CompositeTerm;
            Assert.IsNotNull (term);
            var components = term.SubTerms["components"] as ListTerm;
            Assert.IsNotNull (components);
            Assert.AreEqual (3, components.Terms.Count);
            Assert.AreEqual ("xxx", ((KeywordTerm) components.Terms [0]).Name);
            Assert.AreEqual ("one", ((StringTerm) components.Terms [1]).Value);
            Assert.AreEqual ("xxx", ((KeywordTerm) components.Terms [2]).Name);
            Assert.AreEqual (new SourceInformation (null, 0, 13), term.SourceInformation);
        }

        [Test]
        public void ShouldNotParseStringWithEscapeAtEndOfFile () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""on\", 0, new ParseContext ());

            Assert.IsNull (result);
        }

        [Test]
        public void ShouldNotParseStringWithEscapeCharacterAtEndOfFile () {
            var terminal = new InterpolatedStringProduction (null);
            var result = terminal.Parse (@"""on\""", 0, new ParseContext ());

            Assert.IsNull (result);
        }

        class FakeKeywordProduction : Production {
            private int Length;

            public FakeKeywordProduction (int length) {
                Length = length;
            }

            protected override ParseResult ReallyParse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
                if (index + 3 >= source.Length) {
                    return null;
                }

                var sinfo = parseEnvironment.SourceFileInformation.CreateSourceInformation (index, Length);
                var term = new KeywordTerm (new string (source, index, Length), sinfo);
                return new ParseResult (index + Length, term, context);
            }
        }
    }
}